﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace RssSpeaker
{
	public partial class RssSpeakerForm : Form
	{
		#region props
		private DateTime lastDate = DateTime.Today.Date;
		public static volatile int dldnow;
		private bool inProcess = false;
		private bool onPause = false;
		SpeechRecognitionEngine reco;
		public BlockingCollection<string> queue = new BlockingCollection<string>();
		private bool reallyQuit = false;
		private System.Threading.Timer timer;
		private int cutLongSummaryTo = 300;

		private bool OnPause
		{
			get { return onPause; }
			set {
				onPause = value;
				if (onPause)
					AddLog("Stopped");
			}
		}

		#endregion

		public RssSpeakerForm()
		{
			InitializeComponent();

			try
			{
				reco = SpeechReco.GetReco();
				reco.SpeechRecognized += speechRecognitionWithChoices_SpeechRecognized;
				reco.SpeechHypothesized += reco_SpeechHypothesized;
				reco.SetInputToDefaultAudioDevice();
				reco.EndSilenceTimeout = TimeSpan.FromSeconds(1);
			}
			catch
			{
				AddLog("SpeechRecognizer engine failed to initialize, probably you are missing some components or your OS is not supported!");
			}

			textBox1.Text = System.Configuration.ConfigurationManager.AppSettings["RssURL"];
			int CheckInterval = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["CheckInterval"]); // minutes!
			cutLongSummaryTo = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["CutLongSummaryTo"]); //letters!

			timer = new System.Threading.Timer(new TimerCallback(elapsed), dldnow, 1000 * 1, 1000 * 60 * CheckInterval);

		}

		
		private void Go_Click(object sender, EventArgs e)
		{
			ReadRssAsync(true);
		}

		public void elapsed(object obj)
		{
			ReadRssAsync(false);
		}


		#region

		public async void ReadRssAsync(bool forceStart)
		{
			if (forceStart)
				OnPause = false;
			
			// This method runs asynchronously.
			await Task.Run(() => ReadRss());
		}

		#endregion

		private void ReadRss()
		{
			if (inProcess || OnPause)
				return;

			inProcess = true;
			try
			{
				string url = textBox1.Text;
				XmlReader reader = XmlReader.Create(url);
				SyndicationFeed feed = SyndicationFeed.Load(reader);
				reader.Close();

				bool firstTime = true;
				var feedList = feed.Items.Where(x => x.PublishDate.DateTime > lastDate).OrderBy(x => x.PublishDate);
				if (feedList.Count() > 0)
				{
					foreach (SyndicationItem item in feedList)
					{
						if (OnPause)
							break;

						CheckForQuit();

						if (firstTime)
						{
							string msg = "I have " + feedList.Count() + " new messages!";
							AddLog(msg);
							Speak(msg);
							firstTime = false;
						}

						if (lastDate < item.PublishDate.DateTime)
						{
							String subject = item.Title == null ? "" : item.Title.Text;
							String summary = item.Summary == null ? "no details" : item.Summary.Text;
							DateTime date = item.PublishDate.DateTime;

							if (subject == "")
								continue;

							SpeakOk(subject);

							CheckForQuit();
							if (onPause)
								break;

							lastDate = item.PublishDate.DateTime;

							//reco.RecognizeAsync(RecognizeMode.Multiple); // recognize speech
							//Console.Beep();

							if (reco != null)
							{
								AddLog("waiting for command..");
								Thread.Sleep(500);

								RecognitionResult res = reco.Recognize(TimeSpan.FromMilliseconds(2000)); // recognize speech

								if (res != null && res.Words.Count > 0)
								{
									if (res.Words[0].Text.ToLower() == SpeechReco.Details.ToLower())
									{
										AddLog("Details - command received");
										SpeakOk("Details");
										Speak(summary.Length > cutLongSummaryTo ? summary.Substring(0, cutLongSummaryTo) : summary);
									}
									else if (res.Words[0].Text.ToLower() == SpeechReco.Stop.ToLower())
									{
										AddLog("Stop command received");
										SpeakOk("Yes sir, I will stop till next time");
										break;
									}
									else
										AddLog("Commands not detected..");

								}
							}

							CheckForQuit();

							Thread.Sleep(1000);
						}
					}
				}
				else
				{
					AddLog("No new messages in RSS");
				}
			}
			catch (Exception ex)
			{
				AddLog("There was a problem with rss: " + ex.Message);
			}
			finally
			{
				inProcess = false;
			}
		}

		private void CheckForQuit()
		{
			if (reallyQuit)
				Application.Exit();
		}

		private void AddLog(string message)
		{
			if (reallyQuit)
				return;

			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => { AddLog(message); }));
				return;
			}

			try
			{
				LogBox.AppendText("\r\n" + DateTime.Now.ToString("HH:mm:ss") + "\t" + message);
			}
			catch
			{ }
		}

		private void speechRecognitionWithChoices_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
		{
			AddLog(e.Result.Text);
		}
		void reco_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
		{
			AddLog(">> probably you said " + e.Result.Text);
		}


		private void Speak(string text, string suffix)
		{
			SpeechSynthesizer synth = new SpeechSynthesizer();
			synth.Rate = 2;
			PromptBuilder song = new PromptBuilder();
			song.AppendText(text + ". " + suffix);

			synth.Speak(song);
		}

		private void Speak(string text)
		{
			Speak(text, "");
		}

		private void SpeakOk(string text)
		{
			Speak(text, "Ok");
		}

		private void StopBtn_Click(object sender, EventArgs e)
		{
			OnPause = true;
		}

		private void button1_Click(object sender, EventArgs e)
		{
			reco.RecognizeAsync(RecognizeMode.Multiple);
		}

		private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			if (this.Visible)
			{
				this.Hide();
			}
			else
			{
				this.Show();
				this.WindowState = FormWindowState.Normal;
				this.Activate();
			}
		}

		private void RssSpeakerForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (!reallyQuit)
			{
				this.Hide();
				e.Cancel = true;
			}
		}

		private void RssSpeakerForm_VisibleChanged(object sender, EventArgs e)
		{
			
		}

		private void RssSpeakerForm_Resize(object sender, EventArgs e)
		{
			if (WindowState == FormWindowState.Minimized)
			{
				this.Hide();
			}
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			reallyQuit = true;
			if (!inProcess)
			{
				Application.Exit();
			}
		}

		private void StopMenuItem_Click(object sender, EventArgs e)
		{
			OnPause = true;
		}

		private void GoMenuItem_Click(object sender, EventArgs e)
		{
			ReadRssAsync(true);
		}
	}
}
